import datetime
import socket
import tkinter as tk
import binascii
from gmssl import sm2,sm3
from gmssl import *
from certdiag import *

class globalVal_cert:#身份证书机制下的专用全局变量区
	trustedusers=[]#受信任用户对象的列表
	trusteduserIDs=[]#受信任用户的用户名的列表
	localcertificates=[]#本地所持有的数字证书
	localID=''#本地用户名
	localprivatekey=''#本地私钥
	localpublickey=''#本地公钥
	localuser=None#本地的受信任用户对象
	localuserpasswordhash=''#本地用户密码的哈希

class usercertificate:#用户数字证书（不含私钥）
	def __init__(self,ID,publickey,signerID='',signtime='',signerpublickey=''):
		self.ID=ID#证书持有人用户名
		self.publickey=publickey#证书持有人公钥
		self.signerID=signerID#颁发者用户名
		self.signerpublickey=signerpublickey
		self.signtime=None#颁发者签名动作发生的时间
		self.starttime=None#证书生效时间
		self.endtime=None#证书废除时间
		self.signvalue=None#签名值

	def sign(self,signer_privatekey):#证书颁发者进行签名动作
		t=self.ID+"has"+self.publickey#证书持有人拥有该公钥的声明，也是被签名的内容
		SM2tool=sm2.CryptSM2(public_key=globalVal_cert.localpublickey,private_key=signer_privatekey)#建立临时签名机
		random_hex_str=func.random_hex(SM2tool.para_len)#临时签名机
		self.signvalue=SM2tool.sign(bytes(t.encode('utf-8')),random_hex_str)#使用签名机签名
		self.signtime=datetime.datetime.now()#记录当前时间即为签名动作发生的时间
		self.starttime=datetime.datetime.now()#签名之时，证书开始生效
		self.endtime=self.starttime
		self.endtime=self.endtime.replace(year=self.starttime.year+1)#设定1年有效期
	
	def verify(self,signer_publickey):#对证书颁发者验签，成功则返回True
		t=self.ID+"has"+self.publickey#验签的内容
		SM2tool=sm2.CryptSM2(public_key=signer_publickey,private_key=None)#建立临时验签机，私钥无需故此处不写
		r=SM2tool.verify(self.signvalue,bytes(t.encode('utf-8')))#验签
		t=datetime.datetime.now()#有效期检查
		if t<self.starttime or t>self.endtime:
			return False
		else:
			return r#在有效期内则返回验签机的验签结果，否则宣布失败

class trusteduser:#受信任用户
	def __init__(self,ID,publickey):
		self.ID=ID#用户名，唯一标识，不可重复
		self.publickey=publickey#公钥
		#私钥不公开，故此处不写
		self.certificate=None#默认不持有证书

def loadlocaluser():#加载本地用户的信息
	file=open('localuser.txt','r')#打开载有本地用户信息的文件
	sessionname=file.readline()#获取ID
	if sessionname=='[username]=\n':
		globalVal_cert.localID=file.readline()[:-1]#顺便把结尾的换行符去掉
	sessionname=file.readline()#获取私钥
	if sessionname=='[privatekey]=\n':
		globalVal_cert.localprivatekey=file.readline()[:-1]
	sessionname=file.readline()#获取公钥
	if sessionname=='[publickey]=\n':
		globalVal_cert.localpublickey=file.readline()[:-1]
	globalVal_cert.localuser=trusteduser(globalVal_cert.localID,globalVal_cert.localpublickey)
	sessionname=file.readline()#获取登录密码的哈希值
	if sessionname=="[passwordhash]=\n":
		globalVal_cert.localuserpasswordhash=file.readline()[:-1]
	
	signerID=''
	signerpublickey=''
	signvalue=''
	starttime=None
	endtimetime=None
	signtime=None
	
	sessionname=file.readline()#获取自己证书的颁发者用户名
	if sessionname=="[signerID]=\n":
		signerID=file.readline()[:-1]
	sessionname=file.readline()#获取自己证书的颁发者公钥值
	if sessionname=="[signerpublickey]=\n":
		signerpublickey=file.readline()[:-1]
	sessionname=file.readline()#获取签名值
	if sessionname=="[signvalue]=\n":
		signvalue=file.readline()[:-1]
	sessionname=file.readline()#获取自己证书的开始生效时间
	if sessionname=="[starttime]=\n":
		starttime=datetime.datetime.strptime(file.readline()[:-1],"%Y-%m-%d %H:%M:%S")
	sessionname=file.readline()#获取自己证书的停止有效时间
	if sessionname=='[endtime]=\n':
		endtime=datetime.datetime.strptime(file.readline()[:-1],"%Y-%m-%d %H:%M:%S")
	sessionname=file.readline()#获取自己证书的签名时间
	if sessionname=='[signtime]=\n':
		signtime=datetime.datetime.strptime(file.readline()[:-1],"%Y-%m-%d %H:%M:%S")
	

	mycert=usercertificate(globalVal_cert.localID,globalVal_cert.localpublickey,
		signerID=signerID,signerpublickey=signerpublickey)#建立身份证书对象
	mycert.signvalue=signvalue
	mycert.starttime=starttime
	mycert.endtime=endtime
	mycert.signtime=signtime
	globalVal_cert.localcertificates.append(mycert)#该证书加入到本地证书列表里
	me=trusteduser(globalVal_cert.localID,globalVal_cert.localpublickey)
	me.certificate=mycert
	globalVal_cert.trustedusers.append(me)
	globalVal_cert.trusteduserIDs.append(globalVal_cert.localID)

def localtrustedusers():#加载受信任用户的用户名
	file=open('friends.txt','r')#打开人际关系表的文件
	while True:
		sessionname=file.readline()[:-1]
		if sessionname!='[username]=':
			return
		uname=file.readline()[:-1]#获取受信任用户的ID
		sessionname=file.readline()[:-1]
		if sessionname!='[userpublickey]=':
			return
		upublickey=file.readline()[:-1]#获取受信任用户的公钥
		sessionname=file.readline()[:-1]
		if sessionname!='[signerID]=':
			return
		signerID=file.readline()[:-1]#获取颁发者的用户名
		sessionname=file.readline()[:-1]
		if sessionname!='[signerpublickey]=':
			return
		signerpublickey=file.readline()[:-1]#获取颁发者的公钥
		sessionname=file.readline()[:-1]
		if sessionname!='[starttime]=':
			return
		starttime=datetime.datetime.strptime(file.readline()[:-1],"%Y-%m-%d %H:%M:%S")#证书开始生效时间
		sessionname=file.readline()[:-1]
		if sessionname!='[endtime]=':
			return
		endtime=datetime.datetime.strptime(file.readline()[:-1],"%Y-%m-%d %H:%M:%S")#证书停止有效时间
		sessionname=file.readline()[:-1]
		if sessionname!='[signtime]=':
			return
		signtime=datetime.datetime.strptime(file.readline()[:-1],"%Y-%m-%d %H:%M:%S")#签名时间
		
		u=trusteduser(uname,upublickey)#建立受信任用户对象
		cert=usercertificate(uname,upublickey,signerID=signerID,signtime=signtime,signerpublickey=signerpublickey)
		cert.starttime=starttime#建立证书对象
		cert.endtime=endtime
		cert.signtime=signtime
		u.certificate=cert
		globalVal_cert.trustedusers.append(u)#将读取到的受信任用户加入到受信任用户表中
		globalVal_cert.trusteduserIDs.append(signerID)#记下受信任用户的用户名，接下来会在接收连接建立请求时使用
		sessionname=file.readline()[:-1]
		if not sessionname:#文件读完，则函数关闭
			return

def askcertificate_win():#申请通信用的数字证书，图形界面
	
	def askcertificate(recvIP,recvport):#申请通信用的数字证书，实质内容
		sk=socket.socket()#证书申请用的套接字
		try:
			sk.connect((recvIP,recvport))
		except socket.timeout:
			errorwin2("连接已超时或对方拒绝连接")
		
		sk.send(b'ask to sign')#发起证书颁发请求
		recvmessage=sk.recv(12)
		if recvmessage!=b'ask your ID':#确认颁发者是否在向自身询问自己的用户名
			askcertificate_error(sk)
			return
		sk.send(bytes(globalVal_cert.localID.encode('utf-8')))#发送自身用户名
		recvmessage=sk.recv(20)
		if recvmessage!=b'ask your public key':#确认颁发者是否在询问自己的公钥
			askcertificate_error(sk)
			return
		sk.send(bytes(globalVal_cert.localpublickey.encode('utf-8')))#发送自身公钥
		signvalue=sk.recv(4096).decode('utf-8')#获取签名值，不超过4096字节
		sk.send(b'ask signer ID')#询问颁发者的用户名
		signerID=sk.recv(256).decode('utf-8')#获取颁发者用户名
		
		for u in globalVal_cert.trustedusers:#如果对方已经是一个受信任用户，就无需再次建立证书
			if u.ID==signerID:
				sk.close()
				errorwin2("对方已经是受信任用户")
				return
		
		sk.send(b'ask signer publickey')#询问颁发者公钥
		signerpublickey=sk.recv(4096).decode('utf-8')#获取颁发者公钥
		#时间交互处理
		sk.send(b'ask starttime')#询问证书开始生效的时间
		starttime=datetime.datetime.strptime(sk.recv(30).decode('utf-8'),"%Y-%m-%d %H:%M:%S")
		sk.send(b'ask endtime')#询问证书有效期的截止时间
		endtime=datetime.datetime.strptime(sk.recv(30).decode('utf-8'),"%Y-%m-%d %H:%M:%S")
		sk.send(b'ask signtime')#询问颁发人签名的时间，同时也是证书颁发的时间
		signtime=datetime.datetime.strptime(sk.recv(30).decode('utf-8'),"%Y-%m-%d %H:%M:%S")
		
		
		thecertificate=usercertificate(globalVal_cert.localID,globalVal_cert.localpublickey,signerID=signerID)#建立证书对象
		thecertificate.starttime=starttime#设置时间参数
		thecertificate.endtime=endtime
		thecertificate.signtime=signtime
		thecertificate.signvalue=signvalue#记载签名值
		if thecertificate.verify(globalVal_cert.localpublickey)==False:#当场验签
			sk.send(b'fail to verify')#宣告验签失败
			sk.close()
			errorwin2("证书验签失败")
			return
		sk.send(b'success to verify')#宣布成功验签
		message=sk.recv(10)
		if message!=b'OK':
			sk.close()
			errorwin2("证书验签失败")
			return
		
		globalVal_cert.localcertificates.append(thecertificate)#本证书加入个人数字证书列表
		thesigner=trusteduser(signerID,signerpublickey)
		thesigner.certificate=thecertificate
		globalVal_cert.trustedusers.append(thesigner)#宣布颁发人为受信任用户
		globalVal_cert.trusteduserIDs.append(signerID)
		f=open('friends.txt','a')#打开受信任用户记载的文件，加以记载证书内容
		f.write("[username]=\n"+signerID+'\n[userpublickey]=\n'+signerpublickey+"\n[signerID]=\n"+signerID)
		f.write("\n[signerpublickey]=\n"+signerpublickey)
		f.write("\n[signvalue]=\n"+signvalue)
		f.write("\n[starttime]=\n"+starttime.strftime('%Y-%m-%d %H:%M:%S'))
		f.write("\n[endtime]=\n"+endtime.strftime('%Y-%m-%d %H:%M:%S'))
		f.write("\n[signtime]=\n"+signtime.strftime('%Y-%m-%d %H:%M:%S'))
		f.close()

	askcertwin=tk.Toplevel()
	askcertwin.geometry("300x150+500+500")
	askcertwin.title('用户数字证书申请')
	
	prompt0=tk.Label(askcertwin,text="请输入要发送证书申请的接收方")
	prompt1=tk.Label(askcertwin,text="接收方IP（或主机名）")
	prompt2=tk.Label(askcertwin,text="接收方申请端口号")
	prompt3=tk.Label(askcertwin,text="默认接收端口6543")
	recvIPin=tk.Entry(askcertwin,width="15")
	recvportin=tk.Entry(askcertwin,width="15")
	askbutton=tk.Button(askcertwin,text="发送请求",
		command=lambda:askcertificate(recvIPin.get(),(int)(recvportin.get())))
	
	prompt0.grid(row=0,column=0,columnspan=2)
	prompt1.grid(row=1,column=0)
	prompt2.grid(row=2,column=0)
	prompt3.grid(row=3,column=0,columnspan=2)
	recvIPin.grid(row=1,column=1)
	recvportin.grid(row=2,column=1)
	askbutton.grid(row=4,column=0,columnspan=2)
	if recvportin.get()=='':
		recvportin.insert(tk.END,"6543")

def recvcertificate(selfIP,selfport,activies):#接收证书颁发的请求，实质内容
	def acceptcert():
		connect.send(b'OK')
		f=open('friends.txt','a')#打开受信任用户记载的文件，证书持有者成为受信任用户
		f.write("[username]=\n"+userID.decode('utf-8')+'\n[userpublickey]=\n'+userpublickey.decode('utf-8'))
		f.write("\n[signerID]=\n"+globalVal_cert.localID)
		f.write("\n[signerpublickey]=\n"+globalVal_cert.localpublickey)
		f.write("\n[signvalue]=\n"+thecertificate.signvalue)
		f.write("\n[starttime]=\n"+thecertificate.starttime.strftime('%Y-%m-%d %H:%M:%S'))
		f.write("\n[endtime]=\n"+thecertificate.endtime.strftime('%Y-%m-%d %H:%M:%S'))
		f.write("\n[signtime]=\n"+thecertificate.signtime.strftime('%Y-%m-%d %H:%M:%S'))
		f.close()
		
		requester=trusteduser(userID.decode('utf-8'),userpublickey.decode('utf-8'))
		requester.signerID=globalVal_cert.localID
		requester.certificate=thecertificate#自己颁发证书的对方，也是受信任用户
		globalVal_cert.trustedusers.append(requester)
		
		connect.close()#完事后关闭连接
		sk.close()

	sk=socket.socket()
	sk.bind((selfIP,selfport))
	sk.listen(1)
	try:
		connect,address=sk.accept()
	except socket.timeout:
		return
	
	askmessage=connect.recv(12)
	if askmessage!=b'ask to sign':#确认这真的是一个证书颁发的请求
		sk.close()
		askcertificate_error(connect)
		return
	connect.send(b'ask your ID')#询问对方ID
	userID=connect.recv(256)#获取ID
	connect.send(b'ask your public key')#询问对方公钥
	userpublickey=connect.recv(4096)#获取公钥
	thecertificate=usercertificate(userID.decode('utf-8'),userpublickey.decode('utf-8'),
		signerID=globalVal_cert.localID)
	thecertificate.sign(globalVal_cert.localprivatekey)#签名
	connect.send(bytes(thecertificate.signvalue.encode('utf-8')))#发送签名值
	message=connect.recv(20)
	if message!=b'ask signer ID':
		sk.close()
		askcertificate_error(connect)
		return
	connect.send(bytes(globalVal_cert.localID.encode('utf-8')))#告知申请者自己的用户名
	message=connect.recv(30)
	if message!=b'ask signer publickey':
		sk.close()
		askcertificate_error(connect)
		return
	connect.send(bytes(globalVal_cert.localpublickey.encode('utf-8')))#告诉申请者自己的公钥值
	#时间交互处理
	message=connect.recv(20)
	if message!=b'ask starttime':
		sk.close()
		askcertificate_error(connect)
		return
	connect.send(bytes(thecertificate.starttime.strftime('%Y-%m-%d %H:%M:%S').encode('utf-8')))
	message=connect.recv(20)
	if message!=b'ask endtime':
		sk.close()
		askcertificate_error(connect)
		return
	connect.send(bytes(thecertificate.endtime.strftime('%Y-%m-%d %H:%M:%S').encode('utf-8')))
	message=connect.recv(20)
	if message!=b'ask signtime':
		sk.close()
		askcertificate_error(connect)
		return
	connect.send(bytes(thecertificate.signtime.strftime('%Y-%m-%d %H:%M:%S').encode('utf-8')))
	
	message=connect.recv(30)
	if message!=b'success to verify':#确认要验签成功，反之报错
		errorwin2("证书验签失败")
		return
	
	else:
	
		TextID=activies[0]
		Textpublickey=activies[1]
		promptidentify=activies[2]
		validatebutton=activies[3]
		TextID.insert(tk.END,userID.decode('utf-8'))
		Textpublickey.insert(tk.END,userpublickey.decode('utf-8'))
		r=connect.getpeername()
		promptidentify.config(text="申请方主机："+r[0]+" 端口号："+(str)(r[1]))
		validatebutton.config(command=lambda:acceptcert())

def recvcertificate_win():#收到证书颁发的请求，图形界面
	recvcertwin=tk.Toplevel()
	recvcertwin.geometry('350x300')
	recvcertwin.title('用户数字证书接受申请')
	
	prompt0=tk.Label(recvcertwin,text="请输入要自身作为接收方的相关信息")
	prompt1=tk.Label(recvcertwin,text="IP（或主机名）")
	prompt2=tk.Label(recvcertwin,text="申请端口号")
	prompt3=tk.Label(recvcertwin,text="证书申请的默认接收端口为6543")
	prompt4=tk.Label(recvcertwin,text="")
	recvIPin=tk.Entry(recvcertwin,width=20)
	recvportin=tk.Entry(recvcertwin,width=20)
	
	promptuserID=tk.Label(recvcertwin,text="申请方用户名")
	promptuserpublickey=tk.Label(recvcertwin,text="申请方公钥")
	TextID=tk.Text(recvcertwin,width=20,height=1)
	Textpublickey=tk.Text(recvcertwin,width=20,height=8)
	v=tk.BooleanVar()
	decisionT=tk.Radiobutton(recvcertwin,text="接受",variable=v,value=True)
	decisionF=tk.Radiobutton(recvcertwin,text="拒绝",variable=v,value=False)
	validatebutton=tk.Button(recvcertwin,text="确认选择")
	
	prompt0.grid(row=0,column=0,columnspan=2)
	prompt1.grid(row=1,column=0)
	prompt2.grid(row=2,column=0)
	prompt3.grid(row=3,column=0,columnspan=2)
	promptuserID.grid(row=4,column=0)
	promptuserpublickey.grid(row=5,column=0)
	recvIPin.grid(row=1,column=1)
	recvportin.grid(row=2,column=1)
	TextID.grid(row=4,column=1)
	Textpublickey.grid(row=5,column=1)
	decisionT.grid(row=6,column=0)
	decisionF.grid(row=6,column=1)
	validatebutton.grid(row=6,column=2)
	activies=[TextID,Textpublickey,prompt4,validatebutton]
	recvbutton=tk.Button(recvcertwin,text="等待申请",command=lambda:recvcertificate(recvIPin.get(),
		(int)(recvportin.get()),activies))
	recvbutton.grid(row=2,column=2)
	
	if recvportin.get()=='':
		recvportin.insert(tk.END,"6543")

def makerecveiveconnection_certverify(sk):#建立接收连接时的证书核验，实质内容
	message=sk.recv(20)
	if message!=b'certificate verify':#确认收到核验请求，开始核验
		return False
	sk.send(b'ask userID')#询问对方用户名
	userID=sk.recv(256).decode('utf-8')
	
	sk.send(b'ask user publickey')#询问对方公钥
	userpublickey=sk.recv(4096).decode('utf-8')
	
	sk.send(b'ask signer ID')#询问对方证书的颁发者用户名
	signerID=sk.recv(256).decode('utf-8')
	
	sk.send(b'ask signer publickey')#询问颁发者公钥
	signerpublickey=sk.recv(4096).decode('utf-8')
	
	sk.send(b'ask sign value')#询问签名值
	signvalue=sk.recv(4096).decode('utf-8')
	#证书内的时间交互处理
	sk.send(b'ask starttime')
	starttime=datetime.datetime.strptime(sk.recv(30).decode('utf-8'),"%Y-%m-%d %H:%M:%S")
	sk.send(b'ask endtime')
	endtime=datetime.datetime.strptime(sk.recv(30).decode('utf-8'),"%Y-%m-%d %H:%M:%S")
	sk.send(b'ask signtime')
	signtime=datetime.datetime.strptime(sk.recv(30).decode('utf-8'),"%Y-%m-%d %H:%M:%S")
	
	tempcert=usercertificate(userID,userpublickey,signerID=signerID,signerpublickey=signerpublickey)
	tempcert.starttime=starttime#建立证书对象
	tempcert.endtime=endtime
	tempcert.signtime=signtime
	tempcert.signvalue=signvalue
	try:
		r=tempcert.verify(signerpublickey)#实施验签动作
		if r==True and (signerID in globalVal_cert.trusteduserIDs)or(userID in globalVal_cert.trusteduserIDs):
			sk.send(b'OK')#对方是受信任用户 或 证书验签成功且颁发者为受信任用户 则接受建立连接
			if userID not in globalVal_cert.trusteduserIDs:#如果对方暂不是受信任用户，则使其成为受信任用户
				globalVal_cert.trusteduserIDs.append(userID)
			return True
		else:#核验失败则拒绝通信
			sk.send(b'fail to verify')
			return False
	except socket.timeout:
		errorwin2("连接已超时，请重新启动接收器")
		return False

def makesendeiveconnection_certverify(sk,cert):#建立发送连接时的证书核验

	sk.send(b'certificate verify')#发起请求
	message=sk.recv(15)
	if message!=b'ask userID':
		return False
	sk.send(bytes(globalVal_cert.localID.encode('utf-8')))#发送本地用户名
	message=sk.recv(20)
	if message!=b'ask user publickey':
		return False
	sk.send(bytes(globalVal_cert.localpublickey.encode('utf-8')))#发送本地公钥
	message=sk.recv(15)
	if message!=b'ask signer ID':
		return False
	sk.send(bytes(cert.signerID.encode('utf-8')))#发送证书颁发者用户名
	message=sk.recv(30)
	if message!=b'ask signer publickey':
		return False
	sk.send(cert.signerpublickey.encode('utf-8'))#发送颁发者公钥
	message=sk.recv(30)
	if message!=b'ask sign value':
		return False
	sk.send(cert.signvalue.encode("utf-8"))#发送签名值
	
	message=sk.recv(20)#时间交互处理
	if message!=b'ask starttime':
		return False
	sk.send(cert.starttime.strftime('%Y-%m-%d %H:%M:%S').encode('utf-8'))
	message=sk.recv(20)
	if message!=b'ask endtime':
		return False
	sk.send(cert.endtime.strftime('%Y-%m-%d %H:%M:%S').encode('utf-8'))
	message=sk.recv(20)
	if message!=b'ask signtime':
		return False
	sk.send(cert.signtime.strftime('%Y-%m-%d %H:%M:%S').encode('utf-8'))
	
	r=sk.recv(20)
	if r==b'OK':#对方说通过才算通过
		return True
	else:
		return False

def savechatrecord(contentText):
	record=contentText.get("1.0",tk.END)
	recordpath=tk.filedialog.asksavefilename(title='对话记录保存',filetypes=[('All files', '*')])
	recordfile=file.open(recordpath,'w+')
	recordfile.write(record)